package edu.pitt.cs.cs2410pj2.datamodel;

import java.util.ArrayList;

import edu.pitt.cs.cs2410pj2.util.Parameters;

/**
 * L2CacheSet is similar to L1CacheSet, a little different as it does not need
 * to take care of the state information
 * 
 * @author zhangfan
 * 
 */
public class L2CacheSet {
	private int size = 1;
	private int count = 0;
	private int tileId;
	private int setid;
	
	public int getSetid() {
		return setid;
	}

	public void setSetid(int setid) {
		this.setid = setid;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public int getTileId() {
		return tileId;
	}

	public void setTileId(int tileId) {
		this.tileId = tileId;
	}

	public ArrayList<L2CacheEntry> getBlocks() {
		return blocks;
	}

	public void setBlocks(ArrayList<L2CacheEntry> blocks) {
		this.blocks = blocks;
	}

	public void setFull(boolean isFull) {
		this.isFull = isFull;
	}

	private boolean isFull = false;

	public L2CacheSet() {

	}

	public L2CacheSet(Parameters para) {
		size = (int) Math.pow(2, para.a2);
	}
	
	public L2CacheSet(Parameters para, int tileId, int setIndex) {
		size = (int) Math.pow(2, para.a2);
		this.tileId = tileId;
		this.setid = setIndex;
	}

	private ArrayList<L2CacheEntry> blocks = new ArrayList<L2CacheEntry>();

	// Looking for a block in the cacheset, returns null if not found
	public L2CacheEntry findBlock(int cacheTag) {
		for (int i = 0; i < count; i++) {
			if (blocks.get(i).getCacheTag() == cacheTag) {
				return blocks.get(i);
			}
		}
		return null;
	}

	// adding a block, the cycle is the current time cycle
	public void addBlock(int cacheTag, int timeCycle, String data) {
		L2CacheEntry block = new L2CacheEntry();
		block.setCacheTag(cacheTag);
		block.setUpdateTime(timeCycle);
		block.setData(data);
		blocks.add(block);
		count++;
		if (count == size)
			isFull = true;
	}

	// Update the time, supposed to be used together with findBlock
	public void updateCycle(int cacheTag, int timeCycle) {
		for (int i = 0; i < count; i++) {
			if (blocks.get(i).getCacheTag() == cacheTag) {
				blocks.get(i).setUpdateTime(timeCycle);
				return;
			}
		}
		return;
	}

	public boolean isFull() {
		return isFull;
	}

	// Replace in L2 is a little different with L1
	// In L2, we just find the block and leaves it to the memory controller
	// The status of the block is changed within the directory
	public int replaceLRU(int cacheTag, int timeCycle, String data) {
		// find the least recently used	
		int smallestCycle = blocks.get(0).getUpdateTime();
		int smallestEntryIndex = 0;
		for (int i = 0; i < size; i++) {
			if (blocks.get(i).getUpdateTime() < smallestCycle) {
				smallestEntryIndex = i;
				smallestCycle = blocks.get(i).getUpdateTime();
			}
		}
		int replacedCacheTag = blocks.get(smallestEntryIndex).getCacheTag();
		count--;
		blocks.remove(smallestEntryIndex);
		addBlock(cacheTag, timeCycle, data);
		return replacedCacheTag;
	}
	
	public String toString() {
		String msg = "";
		for(int i = 0;i<count;i++) {
			msg += blocks.get(i).toString()+"\n";
		}
		return msg;
	}
}
